package com.myFwk.config;

import com.myFwk.service.UserDetilServiceImpl;
import com.sun.org.apache.xpath.internal.operations.And;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import javax.annotation.Resource;
import java.security.KeyStore;

/**
 * @author GL
 * @version 1.0
 * @date 2021/8/25 22:44
 */
@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetilServiceImpl userDetailsService;


    public AuthorizationServerConfig() {
        super();
    }


    /**
     * 添加第三客户端
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /*
        OAuth 2.0定义了四种授权方式。
        1.授权码模式（authorization code）
        2.简化模式（implicit）
        3.密码模式（resource owner password credentials）
        4.客户端模式（client credentials）
        */
        clients.inMemory()
                //第三方客户端
                .withClient("fwk-api")
                //第三方客户端秘钥
                .secret(passwordEncoder.encode("fwk-secret"))
                //第三方客户端的授权范围
                .scopes("all")
                //token的有效期
                .accessTokenValiditySeconds(3600)
                //授权类型 指的是通过这个授权方式获取token 5种授权类型
                .authorizedGrantTypes("password","refresh_token")
                //refersh_token有效期
                .refreshTokenValiditySeconds(7 * 3600)
                .and()
                .withClient("inside-app")
                .secret(passwordEncoder.encode("inside-secret"))
                .scopes("all")
                //客户端访问模式
                .authorizedGrantTypes("client_credentials")
                .accessTokenValiditySeconds(7 * 3600);
        super.configure(clients);
        /*
         * 保存token方式是在内存中保存
         * 但是当只有一台authorization-server时没有任何问题，但是当我们有多台的时候由于内存无法共享，
         * 故用户登录的数据仅仅保存在一台服务器里面，这就导致某台授权服务器会误判 是否用户登录这个问题
         * */
    }

    /**
     * 配置验证管理器 userDetailsService
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .tokenStore(jwtTokenStore())
                //设置token的存储位置
                .tokenEnhancer(jwtAccessTokenConverter());
        super.configure(endpoints);
    }


    /*
     * 使用Redis方式存储用户token的方式
     *
     * */
//
//    public TokenStore redisTokenStore() {
//        return new RedisTokenStore(redisConnectionFactory);
//
//    }


    public TokenStore jwtTokenStore() {
        JwtTokenStore jwtTokenStore = new JwtTokenStore(jwtAccessTokenConverter());
        return jwtTokenStore;
    }

    /*
     *
     *
     * 首先1 为什么要引入JWT?
     * 1. 验证的时候不用请求授权服务器，减轻服务器的压力
     * 2. 服务器存在状态问题。在登出的时候需要对JWT的TOKEN进行操作
     * 3. token的存储方式
     *
     * */
    //通过JWT的方式获取获取用户信息
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter();
        //读取classPath下面的配置文件
        ClassPathResource classPathResource = new ClassPathResource("myframwork.jks");
        //获取KeyStoreFactory
        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource, "myframwork".toCharArray());
        //给JwtAccessTokenConverter设置一个秘钥
        tokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("myframwork", "myframwork".toCharArray()));
        return tokenConverter;
    }
}
